1   /*
2    * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package com.sun.imageio.plugins.bmp;
27  
28  import java.awt.Point;
29  import java.awt.Rectangle;
30  import java.awt.Transparency;
31  import java.awt.color.ColorSpace;
32  import java.awt.color.ICC_ColorSpace;
33  import java.awt.color.ICC_Profile;
34  import java.awt.image.BufferedImage;
35  import java.awt.image.ColorModel;
36  import java.awt.image.ComponentColorModel;
37  import java.awt.image.ComponentSampleModel;
38  import java.awt.image.DataBuffer;
39  import java.awt.image.DataBufferByte;
40  import java.awt.image.DataBufferInt;
41  import java.awt.image.DataBufferUShort;
42  import java.awt.image.DirectColorModel;
43  import java.awt.image.IndexColorModel;
44  import java.awt.image.MultiPixelPackedSampleModel;
45  import java.awt.image.PixelInterleavedSampleModel;
46  import java.awt.image.Raster;
47  import java.awt.image.SampleModel;
48  import java.awt.image.SinglePixelPackedSampleModel;
49  import java.awt.image.WritableRaster;
50  
51  import javax.imageio.IIOException;
52  import javax.imageio.ImageIO;
53  import javax.imageio.ImageReader;
54  import javax.imageio.ImageReadParam;
55  import javax.imageio.ImageTypeSpecifier;
56  import javax.imageio.metadata.IIOMetadata;
57  import javax.imageio.spi.ImageReaderSpi;
58  import javax.imageio.stream.ImageInputStream;
59  import javax.imageio.event.IIOReadProgressListener;
60  import javax.imageio.event.IIOReadUpdateListener;
61  import javax.imageio.event.IIOReadWarningListener;
62  
63  import java.io.*;
64  import java.nio.*;
65  import java.security.AccessController;
66  import java.security.PrivilegedAction;
67  import java.util.ArrayList;
68  import java.util.Iterator;
69  import java.util.StringTokenizer;
70  
71  import com.sun.imageio.plugins.common.ImageUtil;
72  import com.sun.imageio.plugins.common.I18N;
73  
74  /** This class is the Java Image IO plugin reader for BMP images.
75   *  It may subsample the image, clip the image, select sub-bands,
76   *  and shift the decoded image origin if the proper decoding parameter
77   *  are set in the provided <code>ImageReadParam</code>.
78   *
79   *  This class supports Microsoft Windows Bitmap Version 3-5,
80   *  as well as OS/2 Bitmap Version 2.x (for single-image BMP file).
81   */
82  public class BMPImageReader extends ImageReader implements BMPConstants {
83      // BMP Image types
84      private static final int VERSION_2_1_BIT = 0;
85      private static final int VERSION_2_4_BIT = 1;
86      private static final int VERSION_2_8_BIT = 2;
87      private static final int VERSION_2_24_BIT = 3;
88  
89      private static final int VERSION_3_1_BIT = 4;
90      private static final int VERSION_3_4_BIT = 5;
91      private static final int VERSION_3_8_BIT = 6;
92      private static final int VERSION_3_24_BIT = 7;
93  
94      private static final int VERSION_3_NT_16_BIT = 8;
95      private static final int VERSION_3_NT_32_BIT = 9;
96  
97      private static final int VERSION_4_1_BIT = 10;
98      private static final int VERSION_4_4_BIT = 11;
99      private static final int VERSION_4_8_BIT = 12;
100     private static final int VERSION_4_16_BIT = 13;
101     private static final int VERSION_4_24_BIT = 14;
102     private static final int VERSION_4_32_BIT = 15;
103 
104     private static final int VERSION_3_XP_EMBEDDED = 16;
105     private static final int VERSION_4_XP_EMBEDDED = 17;
106     private static final int VERSION_5_XP_EMBEDDED = 18;
107 
108     // BMP variables
109     private long bitmapFileSize;
110     private long bitmapOffset;
111     private long compression;
112     private long imageSize;
113     private byte palette[];
114     private int imageType;
115     private int numBands;
116     private boolean isBottomUp;
117     private int bitsPerPixel;
118     private int redMask, greenMask, blueMask, alphaMask;
119 
120     private SampleModel sampleModel, originalSampleModel;
121     private ColorModel colorModel, originalColorModel;
122 
123     /** The input stream where reads from */
124     private ImageInputStream iis = null;
125 
126     /** Indicates whether the header is read. */
127     private boolean gotHeader = false;
128 
129     /** The original image width. */
130     private int width;
131 
132     /** The original image height. */
133     private int height;
134 
135     /** The destination region. */
136     private Rectangle destinationRegion;
137 
138     /** The source region. */
139     private Rectangle sourceRegion;
140 
141     /** The metadata from the stream. */
142     private BMPMetadata metadata;
143 
144     /** The destination image. */
145     private BufferedImage bi;
146 
147     /** Indicates whether subsampled, subregion is required, and offset is
148      *  defined
149      */
150     private boolean noTransform = true;
151 
152     /** Indicates whether subband is selected. */
153     private boolean seleBand = false;
154 
155     /** The scaling factors. */
156     private int scaleX, scaleY;
157 
158     /** source and destination bands. */
159     private int[] sourceBands, destBands;
160 
161     /** Constructs <code>BMPImageReader</code> from the provided
162      *  <code>ImageReaderSpi</code>.
163      */
164     public BMPImageReader(ImageReaderSpi originator) {
165         super(originator);
166     }
167 
168     /** Overrides the method defined in the superclass. */
169     public void setInput(Object input,
170                          boolean seekForwardOnly,
171                          boolean ignoreMetadata) {
172         super.setInput(input, seekForwardOnly, ignoreMetadata);
173         iis = (ImageInputStream) input; // Always works
174         if(iis != null)
175             iis.setByteOrder(ByteOrder.LITTLE_ENDIAN);
176         resetHeaderInfo();
177     }
178 
179     /** Overrides the method defined in the superclass. */
180     public int getNumImages(boolean allowSearch) throws IOException {
181         if (iis == null) {
182             throw new IllegalStateException(I18N.getString("GetNumImages0"));
183         }
184         if (seekForwardOnly && allowSearch) {
185             throw new IllegalStateException(I18N.getString("GetNumImages1"));
186         }
187         return 1;
188     }
189 
190     public int getWidth(int imageIndex) throws IOException {
191         checkIndex(imageIndex);
192         readHeader();
193         return width;
194     }
195 
196     public int getHeight(int imageIndex) throws IOException {
197         checkIndex(imageIndex);
198         readHeader();
199         return height;
200     }
201 
202     private void checkIndex(int imageIndex) {
203         if (imageIndex != 0) {
204             throw new IndexOutOfBoundsException(I18N.getString("BMPImageReader0"));
205         }
206     }
207 
208     public void readHeader() throws IOException {
209         if (gotHeader)
210             return;
211 
212         if (iis == null) {
213             throw new IllegalStateException("Input source not set!");
214         }
215         int profileData = 0, profileSize = 0;
216 
217         this.metadata = new BMPMetadata();
218         iis.mark();
219 
220         // read and check the magic marker
221         byte[] marker = new byte[2];
222         iis.read(marker);
223         if (marker[0] != 0x42 || marker[1] != 0x4d)
224             throw new IllegalArgumentException(I18N.getString("BMPImageReader1"));
225 
226         // Read file size
227         bitmapFileSize = iis.readUnsignedInt();
228         // skip the two reserved fields
229         iis.skipBytes(4);
230 
231         // Offset to the bitmap from the beginning
232         bitmapOffset = iis.readUnsignedInt();
233         // End File Header
234 
235         // Start BitmapCoreHeader
236         long size = iis.readUnsignedInt();
237 
238         if (size == 12) {
239             width = iis.readShort();
240             height = iis.readShort();
241         } else {
242             width = iis.readInt();
243             height = iis.readInt();
244         }
245 
246         metadata.width = width;
247         metadata.height = height;
248 
249         int planes = iis.readUnsignedShort();
250         bitsPerPixel = iis.readUnsignedShort();
251 
252         //metadata.colorPlane = planes;
253         metadata.bitsPerPixel = (short)bitsPerPixel;
254 
255         // As BMP always has 3 rgb bands, except for Version 5,
256         // which is bgra
257         numBands = 3;
258 
259         if (size == 12) {
260             // Windows 2.x and OS/2 1.x
261             metadata.bmpVersion = VERSION_2;
262 
263             // Classify the image type
264             if (bitsPerPixel == 1) {
265                 imageType = VERSION_2_1_BIT;
266             } else if (bitsPerPixel == 4) {
267                 imageType = VERSION_2_4_BIT;
268             } else if (bitsPerPixel == 8) {
269                 imageType = VERSION_2_8_BIT;
270             } else if (bitsPerPixel == 24) {
271                 imageType = VERSION_2_24_BIT;
272             }
273 
274             // Read in the palette
275             int numberOfEntries = (int)((bitmapOffset - 14 - size) / 3);
276             int sizeOfPalette = numberOfEntries*3;
277             palette = new byte[sizeOfPalette];
278             iis.readFully(palette, 0, sizeOfPalette);
279             metadata.palette = palette;
280             metadata.paletteSize = numberOfEntries;
281         } else {
282             compression = iis.readUnsignedInt();
283             imageSize = iis.readUnsignedInt();
284             long xPelsPerMeter = iis.readInt();
285             long yPelsPerMeter = iis.readInt();
286             long colorsUsed = iis.readUnsignedInt();
287             long colorsImportant = iis.readUnsignedInt();
288 
289             metadata.compression = (int)compression;
290             metadata.xPixelsPerMeter = (int)xPelsPerMeter;
291             metadata.yPixelsPerMeter = (int)yPelsPerMeter;
292             metadata.colorsUsed = (int)colorsUsed;
293             metadata.colorsImportant = (int)colorsImportant;
294 
295             if (size == 40) {
296                 // Windows 3.x and Windows NT
297                 switch((int)compression) {
298 
299                 case BI_JPEG:
300                 case BI_PNG:
301                     metadata.bmpVersion = VERSION_3;
302                     imageType = VERSION_3_XP_EMBEDDED;
303                     break;
304 
305                 case BI_RGB:  // No compression
306                 case BI_RLE8:  // 8-bit RLE compression
307                 case BI_RLE4:  // 4-bit RLE compression
308 
309                     // Read in the palette
310                     int numberOfEntries = (int)((bitmapOffset-14-size) / 4);
311                     int sizeOfPalette = numberOfEntries * 4;
312                     palette = new byte[sizeOfPalette];
313                     iis.readFully(palette, 0, sizeOfPalette);
314 
315                     metadata.palette = palette;
316                     metadata.paletteSize = numberOfEntries;
317 
318                     if (bitsPerPixel == 1) {
319                         imageType = VERSION_3_1_BIT;
320                     } else if (bitsPerPixel == 4) {
321                         imageType = VERSION_3_4_BIT;
322                     } else if (bitsPerPixel == 8) {
323                         imageType = VERSION_3_8_BIT;
324                     } else if (bitsPerPixel == 24) {
325                         imageType = VERSION_3_24_BIT;
326                     } else if (bitsPerPixel == 16) {
327                         imageType = VERSION_3_NT_16_BIT;
328 
329                         redMask = 0x7C00;
330                         greenMask = 0x3E0;
331                         blueMask =  (1 << 5) - 1;// 0x1F;
332                         metadata.redMask = redMask;
333                         metadata.greenMask = greenMask;
334                         metadata.blueMask = blueMask;
335                     } else if (bitsPerPixel == 32) {
336                         imageType = VERSION_3_NT_32_BIT;
337                         redMask   = 0x00FF0000;
338                         greenMask = 0x0000FF00;
339                         blueMask  = 0x000000FF;
340                         metadata.redMask = redMask;
341                         metadata.greenMask = greenMask;
342                         metadata.blueMask = blueMask;
343                     }
344 
345                     metadata.bmpVersion = VERSION_3;
346                     break;
347 
348                 case BI_BITFIELDS:
349 
350                     if (bitsPerPixel == 16) {
351                         imageType = VERSION_3_NT_16_BIT;
352                     } else if (bitsPerPixel == 32) {
353                         imageType = VERSION_3_NT_32_BIT;
354                     }
355 
356                     // BitsField encoding
357                     redMask = (int)iis.readUnsignedInt();
358                     greenMask = (int)iis.readUnsignedInt();
359                     blueMask = (int)iis.readUnsignedInt();
360                     metadata.redMask = redMask;
361                     metadata.greenMask = greenMask;
362                     metadata.blueMask = blueMask;
363 
364                     if (colorsUsed != 0) {
365                         // there is a palette
366                         sizeOfPalette = (int)colorsUsed*4;
367                         palette = new byte[sizeOfPalette];
368                         iis.readFully(palette, 0, sizeOfPalette);
369 
370                         metadata.palette = palette;
371                         metadata.paletteSize = (int)colorsUsed;
372                     }
373                     metadata.bmpVersion = VERSION_3_NT;
374 
375                     break;
376                 default:
377                     throw new
378                         RuntimeException(I18N.getString("BMPImageReader2"));
379                 }
380             } else if (size == 108 || size == 124) {
381                 // Windows 4.x BMP
382                 if (size == 108)
383                     metadata.bmpVersion = VERSION_4;
384                 else if (size == 124)
385                     metadata.bmpVersion = VERSION_5;
386 
387                 // rgb masks, valid only if comp is BI_BITFIELDS
388                 redMask = (int)iis.readUnsignedInt();
389                 greenMask = (int)iis.readUnsignedInt();
390                 blueMask = (int)iis.readUnsignedInt();
391                 // Only supported for 32bpp BI_RGB argb
392                 alphaMask = (int)iis.readUnsignedInt();
393                 long csType = iis.readUnsignedInt();
394                 int redX = iis.readInt();
395                 int redY = iis.readInt();
396                 int redZ = iis.readInt();
397                 int greenX = iis.readInt();
398                 int greenY = iis.readInt();
399                 int greenZ = iis.readInt();
400                 int blueX = iis.readInt();
401                 int blueY = iis.readInt();
402                 int blueZ = iis.readInt();
403                 long gammaRed = iis.readUnsignedInt();
404                 long gammaGreen = iis.readUnsignedInt();
405                 long gammaBlue = iis.readUnsignedInt();
406 
407                 if (size == 124) {
408                     metadata.intent = iis.readInt();
409                     profileData = iis.readInt();
410                     profileSize = iis.readInt();
411                     iis.skipBytes(4);
412                 }
413 
414                 metadata.colorSpace = (int)csType;
415 
416                 if (csType == LCS_CALIBRATED_RGB) {
417                     // All the new fields are valid only for this case
418                     metadata.redX = redX;
419                     metadata.redY = redY;
420                     metadata.redZ = redZ;
421                     metadata.greenX = greenX;
422                     metadata.greenY = greenY;
423                     metadata.greenZ = greenZ;
424                     metadata.blueX = blueX;
425                     metadata.blueY = blueY;
426                     metadata.blueZ = blueZ;
427                     metadata.gammaRed = (int)gammaRed;
428                     metadata.gammaGreen = (int)gammaGreen;
429                     metadata.gammaBlue = (int)gammaBlue;
430                 }
431 
432                 // Read in the palette
433                 int numberOfEntries = (int)((bitmapOffset-14-size) / 4);
434                 int sizeOfPalette = numberOfEntries*4;
435                 palette = new byte[sizeOfPalette];
436                 iis.readFully(palette, 0, sizeOfPalette);
437                 metadata.palette = palette;
438                 metadata.paletteSize = numberOfEntries;
439 
440                 switch ((int)compression) {
441                 case BI_JPEG:
442                 case BI_PNG:
443                     if (size == 108) {
444                         imageType = VERSION_4_XP_EMBEDDED;
445                     } else if (size == 124) {
446                         imageType = VERSION_5_XP_EMBEDDED;
447                     }
448                     break;
449                 default:
450                     if (bitsPerPixel == 1) {
451                         imageType = VERSION_4_1_BIT;
452                     } else if (bitsPerPixel == 4) {
453                         imageType = VERSION_4_4_BIT;
454                     } else if (bitsPerPixel == 8) {
455                         imageType = VERSION_4_8_BIT;
456                     } else if (bitsPerPixel == 16) {
457                         imageType = VERSION_4_16_BIT;
458                         if ((int)compression == BI_RGB) {
459                             redMask = 0x7C00;
460                             greenMask = 0x3E0;
461                             blueMask = 0x1F;
462                         }
463                     } else if (bitsPerPixel == 24) {
464                         imageType = VERSION_4_24_BIT;
465                     } else if (bitsPerPixel == 32) {
466                         imageType = VERSION_4_32_BIT;
467                         if ((int)compression == BI_RGB) {
468                             redMask   = 0x00FF0000;
469                             greenMask = 0x0000FF00;
470                             blueMask  = 0x000000FF;
471                         }
472                     }
473 
474                     metadata.redMask = redMask;
475                     metadata.greenMask = greenMask;
476                     metadata.blueMask = blueMask;
477                     metadata.alphaMask = alphaMask;
478                 }
479             } else {
480                 throw new
481                     RuntimeException(I18N.getString("BMPImageReader3"));
482             }
483         }
484 
485         if (height > 0) {
486             // bottom up image
487             isBottomUp = true;
488         } else {
489             // top down image
490             isBottomUp = false;
491             height = Math.abs(height);
492         }
493 
494         // Reset Image Layout so there's only one tile.
495         //Define the color space
496         ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
497         if (metadata.colorSpace == PROFILE_LINKED ||
498             metadata.colorSpace == PROFILE_EMBEDDED) {
499 
500             iis.mark();
501             iis.skipBytes(profileData - size);
502             byte[] profile = new byte[profileSize];
503             iis.readFully(profile, 0, profileSize);
504             iis.reset();
505 
506             try {
507                 if (metadata.colorSpace == PROFILE_LINKED &&
508                     isLinkedProfileAllowed() &&
509                     !isUncOrDevicePath(profile))
510                 {
511                     String path = new String(profile, "windows-1252");
512 
513                     colorSpace =
514                         new ICC_ColorSpace(ICC_Profile.getInstance(path));
515                 } else {
516                     colorSpace =
517                         new ICC_ColorSpace(ICC_Profile.getInstance(profile));
518                 }
519             } catch (Exception e) {
520                 colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
521             }
522         }
523 
524         if (bitsPerPixel == 0 ||
525             compression == BI_JPEG || compression == BI_PNG )
526         {
527             // the colorModel and sampleModel will be initialzed
528             // by the  reader of embedded image
529             colorModel = null;
530             sampleModel = null;
531         } else if (bitsPerPixel == 1 || bitsPerPixel == 4 || bitsPerPixel == 8) {
532             // When number of bitsPerPixel is <= 8, we use IndexColorModel.
533             numBands = 1;
534 
535             if (bitsPerPixel == 8) {
536                 int[] bandOffsets = new int[numBands];
537                 for (int i = 0; i < numBands; i++) {
538                     bandOffsets[i] = numBands -1 -i;
539                 }
540                 sampleModel =
541                     new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE,
542                                                     width, height,
543                                                     numBands,
544                                                     numBands * width,
545                                                     bandOffsets);
546             } else {
547                 // 1 and 4 bit pixels can be stored in a packed format.
548                 sampleModel =
549                     new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE,
550                                                     width, height,
551                                                     bitsPerPixel);
552             }
553 
554             // Create IndexColorModel from the palette.
555             byte r[], g[], b[];
556             if (imageType == VERSION_2_1_BIT ||
557                 imageType == VERSION_2_4_BIT ||
558                 imageType == VERSION_2_8_BIT) {
559 
560 
561                 size = palette.length/3;
562 
563                 if (size > 256) {
564                     size = 256;
565                 }
566 
567                 int off;
568                 r = new byte[(int)size];
569                 g = new byte[(int)size];
570                 b = new byte[(int)size];
571                 for (int i=0; i<(int)size; i++) {
572                     off = 3 * i;
573                     b[i] = palette[off];
574                     g[i] = palette[off+1];
575                     r[i] = palette[off+2];
576                 }
577             } else {
578                 size = palette.length/4;
579 
580                 if (size > 256) {
581                     size = 256;
582                 }
583 
584                 int off;
585                 r = new byte[(int)size];
586                 g = new byte[(int)size];
587                 b = new byte[(int)size];
588                 for (int i=0; i<size; i++) {
589                     off = 4 * i;
590                     b[i] = palette[off];
591                     g[i] = palette[off+1];
592                     r[i] = palette[off+2];
593                 }
594             }
595 
596             if (ImageUtil.isIndicesForGrayscale(r, g, b))
597                 colorModel =
598                     ImageUtil.createColorModel(null, sampleModel);
599             else
600                 colorModel = new IndexColorModel(bitsPerPixel, (int)size, r, g, b);
601         } else if (bitsPerPixel == 16) {
602             numBands = 3;
603             sampleModel =
604                 new SinglePixelPackedSampleModel(DataBuffer.TYPE_USHORT,
605                                                  width, height,
606                                                  new int[] {redMask, greenMask, blueMask});
607 
608             colorModel =
609                 new DirectColorModel(colorSpace,
610                                      16, redMask, greenMask, blueMask, 0,
611                                      false, DataBuffer.TYPE_USHORT);
612 
613         } else if (bitsPerPixel == 32) {
614             numBands = alphaMask == 0 ? 3 : 4;
615 
616             // The number of bands in the SampleModel is determined by
617             // the length of the mask array passed in.
618             int[] bitMasks = numBands == 3 ?
619                 new int[] {redMask, greenMask, blueMask} :
620                 new int[] {redMask, greenMask, blueMask, alphaMask};
621 
622                 sampleModel =
623                     new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT,
624                                                      width, height,
625                                                      bitMasks);
626 
627                 colorModel =
628                     new DirectColorModel(colorSpace,
629                                          32, redMask, greenMask, blueMask, alphaMask,
630                                          false, DataBuffer.TYPE_INT);
631         } else {
632             numBands = 3;
633             // Create SampleModel
634             int[] bandOffsets = new int[numBands];
635             for (int i = 0; i < numBands; i++) {
636                 bandOffsets[i] = numBands -1 -i;
637             }
638 
639             sampleModel =
640                 new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE,
641                                                 width, height,
642                                                 numBands,
643                                                 numBands * width,
644                                                 bandOffsets);
645 
646             colorModel =
647                 ImageUtil.createColorModel(colorSpace, sampleModel);
648         }
649 
650         originalSampleModel = sampleModel;
651         originalColorModel = colorModel;
652 
653         // Reset to the start of bitmap; then jump to the
654         //start of image data
655         iis.reset();
656         iis.skipBytes(bitmapOffset);
657         gotHeader = true;
658     }
659 
660     public Iterator getImageTypes(int imageIndex)
661       throws IOException {
662         checkIndex(imageIndex);
663         readHeader();
664         ArrayList list = new ArrayList(1);
665         list.add(new ImageTypeSpecifier(originalColorModel,
666                                         originalSampleModel));
667         return list.iterator();
668     }
669 
670     public ImageReadParam getDefaultReadParam() {
671         return new ImageReadParam();
672     }
673 
674     public IIOMetadata getImageMetadata(int imageIndex)
675       throws IOException {
676         checkIndex(imageIndex);
677         if (metadata == null) {
678             readHeader();
679         }
680         return metadata;
681     }
682 
683     public IIOMetadata getStreamMetadata() throws IOException {
684         return null;
685     }
686 
687     public boolean isRandomAccessEasy(int imageIndex) throws IOException {
688         checkIndex(imageIndex);
689         readHeader();
690         return metadata.compression == BI_RGB;
691     }
692 
693     public BufferedImage read(int imageIndex, ImageReadParam param)
694         throws IOException {
695 
696         if (iis == null) {
697             throw new IllegalStateException(I18N.getString("BMPImageReader5"));
698         }
699 
700         checkIndex(imageIndex);
701         clearAbortRequest();
702         processImageStarted(imageIndex);
703 
704         if (param == null)
705             param = getDefaultReadParam();
706 
707         //read header
708         readHeader();
709 
710         sourceRegion = new Rectangle(0, 0, 0, 0);
711         destinationRegion = new Rectangle(0, 0, 0, 0);
712 
713         computeRegions(param, this.width, this.height,
714                        param.getDestination(),
715                        sourceRegion,
716                        destinationRegion);
717 
718         scaleX = param.getSourceXSubsampling();
719         scaleY = param.getSourceYSubsampling();
720 
721         // If the destination band is set used it
722         sourceBands = param.getSourceBands();
723         destBands = param.getDestinationBands();
724 
725         seleBand = (sourceBands != null) && (destBands != null);
726         noTransform =
727             destinationRegion.equals(new Rectangle(0, 0, width, height)) ||
728             seleBand;
729 
730         if (!seleBand) {
731             sourceBands = new int[numBands];
732             destBands = new int[numBands];
733             for (int i = 0; i < numBands; i++)
734                 destBands[i] = sourceBands[i] = i;
735         }
736 
737         // If the destination is provided, then use it.  Otherwise, create new one
738         bi = param.getDestination();
739 
740         // Get the image data.
741         WritableRaster raster = null;
742 
743         if (bi == null) {
744             if (sampleModel != null && colorModel != null) {
745                 sampleModel =
746                     sampleModel.createCompatibleSampleModel(destinationRegion.x +
747                                                             destinationRegion.width,
748                                                             destinationRegion.y +
749                                                             destinationRegion.height);
750                 if (seleBand)
751                     sampleModel = sampleModel.createSubsetSampleModel(sourceBands);
752                 raster = Raster.createWritableRaster(sampleModel, new Point());
753                 bi = new BufferedImage(colorModel, raster, false, null);
754             }
755         } else {
756             raster = bi.getWritableTile(0, 0);
757             sampleModel = bi.getSampleModel();
758             colorModel = bi.getColorModel();
759 
760             noTransform &=  destinationRegion.equals(raster.getBounds());
761         }
762 
763         byte bdata[] = null; // buffer for byte data
764         short sdata[] = null; // buffer for short data
765         int idata[] = null; // buffer for int data
766 
767         // the sampleModel can be null in case of embedded image
768         if (sampleModel != null) {
769             if (sampleModel.getDataType() == DataBuffer.TYPE_BYTE)
770                 bdata = (byte[])
771                     ((DataBufferByte)raster.getDataBuffer()).getData();
772             else if (sampleModel.getDataType() == DataBuffer.TYPE_USHORT)
773                 sdata = (short[])
774                     ((DataBufferUShort)raster.getDataBuffer()).getData();
775             else if (sampleModel.getDataType() == DataBuffer.TYPE_INT)
776                 idata = (int[])
777                     ((DataBufferInt)raster.getDataBuffer()).getData();
778         }
779 
780         // There should only be one tile.
781         switch(imageType) {
782 
783         case VERSION_2_1_BIT:
784             // no compression
785             read1Bit(bdata);
786             break;
787 
788         case VERSION_2_4_BIT:
789             // no compression
790             read4Bit(bdata);
791             break;
792 
793         case VERSION_2_8_BIT:
794             // no compression
795             read8Bit(bdata);
796             break;
797 
798         case VERSION_2_24_BIT:
799             // no compression
800             read24Bit(bdata);
801             break;
802 
803         case VERSION_3_1_BIT:
804             // 1-bit images cannot be compressed.
805             read1Bit(bdata);
806             break;
807 
808         case VERSION_3_4_BIT:
809             switch((int)compression) {
810             case BI_RGB:
811                 read4Bit(bdata);
812                 break;
813 
814             case BI_RLE4:
815                 readRLE4(bdata);
816                 break;
817 
818             default:
819                 throw new
820                     RuntimeException(I18N.getString("BMPImageReader1"));
821             }
822             break;
823 
824         case VERSION_3_8_BIT:
825             switch((int)compression) {
826             case BI_RGB:
827                 read8Bit(bdata);
828                 break;
829 
830             case BI_RLE8:
831                 readRLE8(bdata);
832                 break;
833 
834             default:
835                 throw new
836                     RuntimeException(I18N.getString("BMPImageReader1"));
837             }
838 
839             break;
840 
841         case VERSION_3_24_BIT:
842             // 24-bit images are not compressed
843             read24Bit(bdata);
844             break;
845 
846         case VERSION_3_NT_16_BIT:
847             read16Bit(sdata);
848             break;
849 
850         case VERSION_3_NT_32_BIT:
851             read32Bit(idata);
852             break;
853 
854         case VERSION_3_XP_EMBEDDED:
855         case VERSION_4_XP_EMBEDDED:
856         case VERSION_5_XP_EMBEDDED:
857             bi = readEmbedded((int)compression, bi, param);
858             break;
859 
860         case VERSION_4_1_BIT:
861             read1Bit(bdata);
862             break;
863 
864         case VERSION_4_4_BIT:
865             switch((int)compression) {
866 
867             case BI_RGB:
868                 read4Bit(bdata);
869                 break;
870 
871             case BI_RLE4:
872                 readRLE4(bdata);
873                 break;
874 
875             default:
876                 throw new
877                     RuntimeException(I18N.getString("BMPImageReader1"));
878             }
879 
880         case VERSION_4_8_BIT:
881             switch((int)compression) {
882 
883             case BI_RGB:
884                 read8Bit(bdata);
885                 break;
886 
887             case BI_RLE8:
888                 readRLE8(bdata);
889                 break;
890 
891             default:
892                 throw new
893                     RuntimeException(I18N.getString("BMPImageReader1"));
894             }
895             break;
896 
897         case VERSION_4_16_BIT:
898             read16Bit(sdata);
899             break;
900 
901         case VERSION_4_24_BIT:
902             read24Bit(bdata);
903             break;
904 
905         case VERSION_4_32_BIT:
906             read32Bit(idata);
907             break;
908         }
909 
910         if (abortRequested())
911             processReadAborted();
912         else
913             processImageComplete();
914 
915         return bi;
916     }
917 
918     public boolean canReadRaster() {
919         return true;
920     }
921 
922     public Raster readRaster(int imageIndex,
923                              ImageReadParam param) throws IOException {
924         BufferedImage bi = read(imageIndex, param);
925         return bi.getData();
926     }
927 
928     private void resetHeaderInfo() {
929         gotHeader = false;
930         bi = null;
931         sampleModel = originalSampleModel = null;
932         colorModel = originalColorModel = null;
933     }
934 
935     public void reset() {
936         super.reset();
937         iis = null;
938         resetHeaderInfo();
939     }
940 
941     // Deal with 1 Bit images using IndexColorModels
942     private void read1Bit(byte[] bdata) throws IOException {
943         int bytesPerScanline = (width + 7) / 8;
944         int padding = bytesPerScanline % 4;
945         if (padding != 0) {
946             padding = 4 - padding;
947         }
948 
949         int lineLength = bytesPerScanline + padding;
950 
951         if (noTransform) {
952             int j = isBottomUp ? (height -1)*bytesPerScanline : 0;
953 
954             for (int i=0; i<height; i++) {
955                 if (abortRequested()) {
956                     break;
957                 }
958                 iis.readFully(bdata, j, bytesPerScanline);
959                 iis.skipBytes(padding);
960                 j += isBottomUp ? -bytesPerScanline : bytesPerScanline;
961                 processImageUpdate(bi, 0, i,
962                                    destinationRegion.width, 1, 1, 1,
963                                    new int[]{0});
964                 processImageProgress(100.0F * i/destinationRegion.height);
965             }
966         } else {
967             byte[] buf = new byte[lineLength];
968             int lineStride =
969                 ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride();
970 
971             if (isBottomUp) {
972                 int lastLine =
973                     sourceRegion.y + (destinationRegion.height - 1) * scaleY;
974                 iis.skipBytes(lineLength * (height - 1 - lastLine));
975             } else
976                 iis.skipBytes(lineLength * sourceRegion.y);
977 
978             int skipLength = lineLength * (scaleY - 1);
979 
980             // cache the values to avoid duplicated computation
981             int[] srcOff = new int[destinationRegion.width];
982             int[] destOff = new int[destinationRegion.width];
983             int[] srcPos = new int[destinationRegion.width];
984             int[] destPos = new int[destinationRegion.width];
985 
986             for (int i = destinationRegion.x, x = sourceRegion.x, j = 0;
987                  i < destinationRegion.x + destinationRegion.width;
988                  i++, j++, x += scaleX) {
989                 srcPos[j] = x >> 3;
990                 srcOff[j] = 7 - (x & 7);
991                 destPos[j] = i >> 3;
992                 destOff[j] = 7 - (i & 7);
993             }
994 
995             int k = destinationRegion.y * lineStride;
996             if (isBottomUp)
997                 k += (destinationRegion.height - 1) * lineStride;
998 
999             for (int j = 0, y = sourceRegion.y;
1000                  j < destinationRegion.height; j++, y+=scaleY) {
1001 
1002                 if (abortRequested())
1003                     break;
1004                 iis.read(buf, 0, lineLength);
1005                 for (int i = 0; i < destinationRegion.width; i++) {
1006                     //get the bit and assign to the data buffer of the raster
1007                     int v = (buf[srcPos[i]] >> srcOff[i]) & 1;
1008                     bdata[k + destPos[i]] |= v << destOff[i];
1009                 }
1010 
1011                 k += isBottomUp ? -lineStride : lineStride;
1012                 iis.skipBytes(skipLength);
1013                 processImageUpdate(bi, 0, j,
1014                                    destinationRegion.width, 1, 1, 1,
1015                                    new int[]{0});
1016                 processImageProgress(100.0F*j/destinationRegion.height);
1017             }
1018         }
1019     }
1020 
1021     // Method to read a 4 bit BMP image data
1022     private void read4Bit(byte[] bdata) throws IOException {
1023 
1024         int bytesPerScanline = (width + 1) / 2;
1025 
1026         // Padding bytes at the end of each scanline
1027         int padding = bytesPerScanline % 4;
1028         if (padding != 0)
1029             padding = 4 - padding;
1030 
1031         int lineLength = bytesPerScanline + padding;
1032 
1033         if (noTransform) {
1034             int j = isBottomUp ? (height -1) * bytesPerScanline : 0;
1035 
1036             for (int i=0; i<height; i++) {
1037                 if (abortRequested()) {
1038                     break;
1039                 }
1040                 iis.readFully(bdata, j, bytesPerScanline);
1041                 iis.skipBytes(padding);
1042                 j += isBottomUp ? -bytesPerScanline : bytesPerScanline;
1043                 processImageUpdate(bi, 0, i,
1044                                    destinationRegion.width, 1, 1, 1,
1045                                    new int[]{0});
1046                 processImageProgress(100.0F * i/destinationRegion.height);
1047             }
1048         } else {
1049             byte[] buf = new byte[lineLength];
1050             int lineStride =
1051                 ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride();
1052 
1053             if (isBottomUp) {
1054                 int lastLine =
1055                     sourceRegion.y + (destinationRegion.height - 1) * scaleY;
1056                 iis.skipBytes(lineLength * (height - 1 - lastLine));
1057             } else
1058                 iis.skipBytes(lineLength * sourceRegion.y);
1059 
1060             int skipLength = lineLength * (scaleY - 1);
1061 
1062             // cache the values to avoid duplicated computation
1063             int[] srcOff = new int[destinationRegion.width];
1064             int[] destOff = new int[destinationRegion.width];
1065             int[] srcPos = new int[destinationRegion.width];
1066             int[] destPos = new int[destinationRegion.width];
1067 
1068             for (int i = destinationRegion.x, x = sourceRegion.x, j = 0;
1069                  i < destinationRegion.x + destinationRegion.width;
1070                  i++, j++, x += scaleX) {
1071                 srcPos[j] = x >> 1;
1072                 srcOff[j] = (1 - (x & 1)) << 2;
1073                 destPos[j] = i >> 1;
1074                 destOff[j] = (1 - (i & 1)) << 2;
1075             }
1076 
1077             int k = destinationRegion.y * lineStride;
1078             if (isBottomUp)
1079                 k += (destinationRegion.height - 1) * lineStride;
1080 
1081             for (int j = 0, y = sourceRegion.y;
1082                  j < destinationRegion.height; j++, y+=scaleY) {
1083 
1084                 if (abortRequested())
1085                     break;
1086                 iis.read(buf, 0, lineLength);
1087                 for (int i = 0; i < destinationRegion.width; i++) {
1088                     //get the bit and assign to the data buffer of the raster
1089                     int v = (buf[srcPos[i]] >> srcOff[i]) & 0x0F;
1090                     bdata[k + destPos[i]] |= v << destOff[i];
1091                 }
1092 
1093                 k += isBottomUp ? -lineStride : lineStride;
1094                 iis.skipBytes(skipLength);
1095                 processImageUpdate(bi, 0, j,
1096                                    destinationRegion.width, 1, 1, 1,
1097                                    new int[]{0});
1098                 processImageProgress(100.0F*j/destinationRegion.height);
1099             }
1100         }
1101     }
1102 
1103     // Method to read 8 bit BMP image data
1104     private void read8Bit(byte[] bdata) throws IOException {
1105 
1106         // Padding bytes at the end of each scanline
1107         int padding = width % 4;
1108         if (padding != 0) {
1109             padding = 4 - padding;
1110         }
1111 
1112         int lineLength = width + padding;
1113 
1114         if (noTransform) {
1115             int j = isBottomUp ? (height -1) * width : 0;
1116 
1117             for (int i=0; i<height; i++) {
1118                 if (abortRequested()) {
1119                     break;
1120                 }
1121                 iis.readFully(bdata, j, width);
1122                 iis.skipBytes(padding);
1123                 j += isBottomUp ? -width : width;
1124                 processImageUpdate(bi, 0, i,
1125                                    destinationRegion.width, 1, 1, 1,
1126                                    new int[]{0});
1127                 processImageProgress(100.0F * i/destinationRegion.height);
1128             }
1129         } else {
1130             byte[] buf = new byte[lineLength];
1131             int lineStride =
1132                 ((ComponentSampleModel)sampleModel).getScanlineStride();
1133 
1134             if (isBottomUp) {
1135                 int lastLine =
1136                     sourceRegion.y + (destinationRegion.height - 1) * scaleY;
1137                 iis.skipBytes(lineLength * (height - 1 - lastLine));
1138             } else
1139                 iis.skipBytes(lineLength * sourceRegion.y);
1140 
1141             int skipLength = lineLength * (scaleY - 1);
1142 
1143             int k = destinationRegion.y * lineStride;
1144             if (isBottomUp)
1145                 k += (destinationRegion.height - 1) * lineStride;
1146             k += destinationRegion.x;
1147 
1148             for (int j = 0, y = sourceRegion.y;
1149                  j < destinationRegion.height; j++, y+=scaleY) {
1150 
1151                 if (abortRequested())
1152                     break;
1153                 iis.read(buf, 0, lineLength);
1154                 for (int i = 0, m = sourceRegion.x;
1155                      i < destinationRegion.width; i++, m += scaleX) {
1156                     //get the bit and assign to the data buffer of the raster
1157                     bdata[k + i] = buf[m];
1158                 }
1159 
1160                 k += isBottomUp ? -lineStride : lineStride;
1161                 iis.skipBytes(skipLength);
1162                 processImageUpdate(bi, 0, j,
1163                                    destinationRegion.width, 1, 1, 1,
1164                                    new int[]{0});
1165                 processImageProgress(100.0F*j/destinationRegion.height);
1166             }
1167         }
1168     }
1169 
1170     // Method to read 24 bit BMP image data
1171     private void read24Bit(byte[] bdata) throws IOException {
1172         // Padding bytes at the end of each scanline
1173         // width * bitsPerPixel should be divisible by 32
1174         int padding = width * 3 % 4;
1175         if ( padding != 0)
1176             padding = 4 - padding;
1177 
1178         int lineStride = width * 3;
1179         int lineLength = lineStride + padding;
1180 
1181         if (noTransform) {
1182             int j = isBottomUp ? (height -1) * width * 3 : 0;
1183 
1184             for (int i=0; i<height; i++) {
1185                 if (abortRequested()) {
1186                     break;
1187                 }
1188                 iis.readFully(bdata, j, lineStride);
1189                 iis.skipBytes(padding);
1190                 j += isBottomUp ? -lineStride : lineStride;
1191                 processImageUpdate(bi, 0, i,
1192                                    destinationRegion.width, 1, 1, 1,
1193                                    new int[]{0});
1194                 processImageProgress(100.0F * i/destinationRegion.height);
1195             }
1196         } else {
1197             byte[] buf = new byte[lineLength];
1198             lineStride =
1199                 ((ComponentSampleModel)sampleModel).getScanlineStride();
1200 
1201             if (isBottomUp) {
1202                 int lastLine =
1203                     sourceRegion.y + (destinationRegion.height - 1) * scaleY;
1204                 iis.skipBytes(lineLength * (height - 1 - lastLine));
1205             } else
1206                 iis.skipBytes(lineLength * sourceRegion.y);
1207 
1208             int skipLength = lineLength * (scaleY - 1);
1209 
1210             int k = destinationRegion.y * lineStride;
1211             if (isBottomUp)
1212                 k += (destinationRegion.height - 1) * lineStride;
1213             k += destinationRegion.x * 3;
1214 
1215             for (int j = 0, y = sourceRegion.y;
1216                  j < destinationRegion.height; j++, y+=scaleY) {
1217 
1218                 if (abortRequested())
1219                     break;
1220                 iis.read(buf, 0, lineLength);
1221                 for (int i = 0, m = 3 * sourceRegion.x;
1222                      i < destinationRegion.width; i++, m += 3 * scaleX) {
1223                     //get the bit and assign to the data buffer of the raster
1224                     int n = 3 * i + k;
1225                     for (int b = 0; b < destBands.length; b++)
1226                         bdata[n + destBands[b]] = buf[m + sourceBands[b]];
1227                 }
1228 
1229                 k += isBottomUp ? -lineStride : lineStride;
1230                 iis.skipBytes(skipLength);
1231                 processImageUpdate(bi, 0, j,
1232                                    destinationRegion.width, 1, 1, 1,
1233                                    new int[]{0});
1234                 processImageProgress(100.0F*j/destinationRegion.height);
1235             }
1236         }
1237     }
1238 
1239     private void read16Bit(short sdata[]) throws IOException {
1240         // Padding bytes at the end of each scanline
1241         // width * bitsPerPixel should be divisible by 32
1242         int padding = width * 2 % 4;
1243 
1244         if ( padding != 0)
1245             padding = 4 - padding;
1246 
1247         int lineLength = width + padding / 2;
1248 
1249         if (noTransform) {
1250             int j = isBottomUp ? (height -1) * width : 0;
1251             for (int i=0; i<height; i++) {
1252                 if (abortRequested()) {
1253                     break;
1254                 }
1255 
1256                 iis.readFully(sdata, j, width);
1257                 iis.skipBytes(padding);
1258 
1259                 j += isBottomUp ? -width : width;
1260                 processImageUpdate(bi, 0, i,
1261                                    destinationRegion.width, 1, 1, 1,
1262                                    new int[]{0});
1263                 processImageProgress(100.0F * i/destinationRegion.height);
1264             }
1265         } else {
1266             short[] buf = new short[lineLength];
1267             int lineStride =
1268                 ((SinglePixelPackedSampleModel)sampleModel).getScanlineStride();
1269 
1270             if (isBottomUp) {
1271                 int lastLine =
1272                     sourceRegion.y + (destinationRegion.height - 1) * scaleY;
1273                 iis.skipBytes(lineLength * (height - 1 - lastLine) << 1);
1274             } else
1275                 iis.skipBytes(lineLength * sourceRegion.y << 1);
1276 
1277             int skipLength = lineLength * (scaleY - 1) << 1;
1278 
1279             int k = destinationRegion.y * lineStride;
1280             if (isBottomUp)
1281                 k += (destinationRegion.height - 1) * lineStride;
1282             k += destinationRegion.x;
1283 
1284             for (int j = 0, y = sourceRegion.y;
1285                  j < destinationRegion.height; j++, y+=scaleY) {
1286 
1287                 if (abortRequested())
1288                     break;
1289                 iis.readFully(buf, 0, lineLength);
1290                 for (int i = 0, m = sourceRegion.x;
1291                      i < destinationRegion.width; i++, m += scaleX) {
1292                     //get the bit and assign to the data buffer of the raster
1293                     sdata[k + i] = buf[m];
1294                 }
1295 
1296                 k += isBottomUp ? -lineStride : lineStride;
1297                 iis.skipBytes(skipLength);
1298                 processImageUpdate(bi, 0, j,
1299                                    destinationRegion.width, 1, 1, 1,
1300                                    new int[]{0});
1301                 processImageProgress(100.0F*j/destinationRegion.height);
1302             }
1303         }
1304     }
1305 
1306     private void read32Bit(int idata[]) throws IOException {
1307         if (noTransform) {
1308             int j = isBottomUp ? (height -1) * width : 0;
1309 
1310             for (int i=0; i<height; i++) {
1311                 if (abortRequested()) {
1312                     break;
1313                 }
1314                 iis.readFully(idata, j, width);
1315                 j += isBottomUp ? -width : width;
1316                 processImageUpdate(bi, 0, i,
1317                                    destinationRegion.width, 1, 1, 1,
1318                                    new int[]{0});
1319                 processImageProgress(100.0F * i/destinationRegion.height);
1320             }
1321         } else {
1322             int[] buf = new int[width];
1323             int lineStride =
1324                 ((SinglePixelPackedSampleModel)sampleModel).getScanlineStride();
1325 
1326             if (isBottomUp) {
1327                 int lastLine =
1328                     sourceRegion.y + (destinationRegion.height - 1) * scaleY;
1329                 iis.skipBytes(width * (height - 1 - lastLine) << 2);
1330             } else
1331                 iis.skipBytes(width * sourceRegion.y << 2);
1332 
1333             int skipLength = width * (scaleY - 1) << 2;
1334 
1335             int k = destinationRegion.y * lineStride;
1336             if (isBottomUp)
1337                 k += (destinationRegion.height - 1) * lineStride;
1338             k += destinationRegion.x;
1339 
1340             for (int j = 0, y = sourceRegion.y;
1341                  j < destinationRegion.height; j++, y+=scaleY) {
1342 
1343                 if (abortRequested())
1344                     break;
1345                 iis.readFully(buf, 0, width);
1346                 for (int i = 0, m = sourceRegion.x;
1347                      i < destinationRegion.width; i++, m += scaleX) {
1348                     //get the bit and assign to the data buffer of the raster
1349                     idata[k + i] = buf[m];
1350                 }
1351 
1352                 k += isBottomUp ? -lineStride : lineStride;
1353                 iis.skipBytes(skipLength);
1354                 processImageUpdate(bi, 0, j,
1355                                    destinationRegion.width, 1, 1, 1,
1356                                    new int[]{0});
1357                 processImageProgress(100.0F*j/destinationRegion.height);
1358             }
1359         }
1360     }
1361 
1362     private void readRLE8(byte bdata[]) throws IOException {
1363         // If imageSize field is not provided, calculate it.
1364         int imSize = (int)imageSize;
1365         if (imSize == 0) {
1366             imSize = (int)(bitmapFileSize - bitmapOffset);
1367         }
1368 
1369         int padding = 0;
1370         // If width is not 32 bit aligned, then while uncompressing each
1371         // scanline will have padding bytes, calculate the amount of padding
1372         int remainder = width % 4;
1373         if (remainder != 0) {
1374             padding = 4 - remainder;
1375         }
1376 
1377         // Read till we have the whole image
1378         byte values[] = new byte[imSize];
1379         int bytesRead = 0;
1380         iis.readFully(values, 0, imSize);
1381 
1382         // Since data is compressed, decompress it
1383         decodeRLE8(imSize, padding, values, bdata);
1384     }
1385 
1386     private void decodeRLE8(int imSize,
1387                             int padding,
1388                             byte[] values,
1389                             byte[] bdata) throws IOException {
1390 
1391         byte val[] = new byte[width * height];
1392         int count = 0, l = 0;
1393         int value;
1394         boolean flag = false;
1395         int lineNo = isBottomUp ? height - 1 : 0;
1396         int lineStride =
1397             ((ComponentSampleModel)sampleModel).getScanlineStride();
1398         int finished = 0;
1399 
1400         while (count != imSize) {
1401             value = values[count++] & 0xff;
1402             if (value == 0) {
1403                 switch(values[count++] & 0xff) {
1404 
1405                 case 0:
1406                     // End-of-scanline marker
1407                     if (lineNo >= sourceRegion.y &&
1408                         lineNo < sourceRegion.y + sourceRegion.height) {
1409                         if (noTransform) {
1410                             int pos = lineNo * width;
1411                             for(int i = 0; i < width; i++)
1412                                 bdata[pos++] = val[i];
1413                             processImageUpdate(bi, 0, lineNo,
1414                                                destinationRegion.width, 1, 1, 1,
1415                                                new int[]{0});
1416                             finished++;
1417                         } else if ((lineNo - sourceRegion.y) % scaleY == 0) {
1418                             int currentLine = (lineNo - sourceRegion.y) / scaleY +
1419                                 destinationRegion.y;
1420                             int pos = currentLine * lineStride;
1421                             pos += destinationRegion.x;
1422                             for (int i = sourceRegion.x;
1423                                  i < sourceRegion.x + sourceRegion.width;
1424                                  i += scaleX)
1425                                 bdata[pos++] = val[i];
1426                             processImageUpdate(bi, 0, currentLine,
1427                                                destinationRegion.width, 1, 1, 1,
1428                                                new int[]{0});
1429                             finished++;
1430                         }
1431                     }
1432                     processImageProgress(100.0F * finished / destinationRegion.height);
1433                     lineNo += isBottomUp ? -1 : 1;
1434                     l = 0;
1435 
1436                     if (abortRequested()) {
1437                         flag = true;
1438                     }
1439 
1440                     break;
1441 
1442                 case 1:
1443                     // End-of-RLE marker
1444                     flag = true;
1445                     break;
1446 
1447                 case 2:
1448                     // delta or vector marker
1449                     int xoff = values[count++] & 0xff;
1450                     int yoff = values[count] & 0xff;
1451                     // Move to the position xoff, yoff down
1452                     l += xoff + yoff*width;
1453                     break;
1454 
1455                 default:
1456                     int end = values[count-1] & 0xff;
1457                     for (int i=0; i<end; i++) {
1458                         val[l++] = (byte)(values[count++] & 0xff);
1459                     }
1460 
1461                     // Whenever end pixels can fit into odd number of bytes,
1462                     // an extra padding byte will be present, so skip that.
1463                     if ((end & 1) == 1) {
1464                         count++;
1465                     }
1466                 }
1467             } else {
1468                 for (int i=0; i<value; i++) {
1469                     val[l++] = (byte)(values[count] & 0xff);
1470                 }
1471 
1472                 count++;
1473             }
1474 
1475             // If End-of-RLE data, then exit the while loop
1476             if (flag) {
1477                 break;
1478             }
1479         }
1480     }
1481 
1482     private void readRLE4(byte[] bdata) throws IOException {
1483 
1484         // If imageSize field is not specified, calculate it.
1485         int imSize = (int)imageSize;
1486         if (imSize == 0) {
1487             imSize = (int)(bitmapFileSize - bitmapOffset);
1488         }
1489 
1490         int padding = 0;
1491         // If width is not 32 byte aligned, then while uncompressing each
1492         // scanline will have padding bytes, calculate the amount of padding
1493         int remainder = width % 4;
1494         if (remainder != 0) {
1495             padding = 4 - remainder;
1496         }
1497 
1498         // Read till we have the whole image
1499         byte[] values = new byte[imSize];
1500         iis.readFully(values, 0, imSize);
1501 
1502         // Decompress the RLE4 compressed data.
1503         decodeRLE4(imSize, padding, values, bdata);
1504     }
1505 
1506     private void decodeRLE4(int imSize,
1507                             int padding,
1508                             byte[] values,
1509                             byte[] bdata) throws IOException {
1510         byte[] val = new byte[width];
1511         int count = 0, l = 0;
1512         int value;
1513         boolean flag = false;
1514         int lineNo = isBottomUp ? height - 1 : 0;
1515         int lineStride =
1516             ((MultiPixelPackedSampleModel)sampleModel).getScanlineStride();
1517         int finished = 0;
1518 
1519         while (count != imSize) {
1520 
1521             value = values[count++] & 0xFF;
1522             if (value == 0) {
1523 
1524 
1525                 // Absolute mode
1526                 switch(values[count++] & 0xFF) {
1527 
1528                 case 0:
1529                     // End-of-scanline marker
1530                     // End-of-scanline marker
1531                     if (lineNo >= sourceRegion.y &&
1532                         lineNo < sourceRegion.y + sourceRegion.height) {
1533                         if (noTransform) {
1534                             int pos = lineNo * (width + 1 >> 1);
1535                             for(int i = 0, j = 0; i < width >> 1; i++)
1536                                 bdata[pos++] =
1537                                     (byte)((val[j++] << 4) | val[j++]);
1538                             if ((width & 1) == 1)
1539                                 bdata[pos] |= val[width - 1] << 4;
1540 
1541                             processImageUpdate(bi, 0, lineNo,
1542                                                destinationRegion.width, 1, 1, 1,
1543                                                new int[]{0});
1544                             finished++;
1545                         } else if ((lineNo - sourceRegion.y) % scaleY == 0) {
1546                             int currentLine = (lineNo - sourceRegion.y) / scaleY +
1547                                 destinationRegion.y;
1548                             int pos = currentLine * lineStride;
1549                             pos += destinationRegion.x >> 1;
1550                             int shift = (1 - (destinationRegion.x & 1)) << 2;
1551                             for (int i = sourceRegion.x;
1552                                  i < sourceRegion.x + sourceRegion.width;
1553                                  i += scaleX) {
1554                                 bdata[pos] |= val[i] << shift;
1555                                 shift += 4;
1556                                 if (shift == 4) {
1557                                     pos++;
1558                                 }
1559                                 shift &= 7;
1560                             }
1561                             processImageUpdate(bi, 0, currentLine,
1562                                                destinationRegion.width, 1, 1, 1,
1563                                                new int[]{0});
1564                             finished++;
1565                         }
1566                     }
1567                     processImageProgress(100.0F * finished / destinationRegion.height);
1568                     lineNo += isBottomUp ? -1 : 1;
1569                     l = 0;
1570 
1571                     if (abortRequested()) {
1572                         flag = true;
1573                     }
1574 
1575                     break;
1576 
1577                 case 1:
1578                     // End-of-RLE marker
1579                     flag = true;
1580                     break;
1581 
1582                 case 2:
1583                     // delta or vector marker
1584                     int xoff = values[count++] & 0xFF;
1585                     int yoff = values[count] & 0xFF;
1586                     // Move to the position xoff, yoff down
1587                     l += xoff + yoff*width;
1588                     break;
1589 
1590                 default:
1591                     int end = values[count-1] & 0xFF;
1592                     for (int i=0; i<end; i++) {
1593                         val[l++] = (byte)(((i & 1) == 0) ? (values[count] & 0xf0) >> 4
1594                                           : (values[count++] & 0x0f));
1595                     }
1596 
1597                     // When end is odd, the above for loop does not
1598                     // increment count, so do it now.
1599                     if ((end & 1) == 1) {
1600                         count++;
1601                     }
1602 
1603                     // Whenever end pixels can fit into odd number of bytes,
1604                     // an extra padding byte will be present, so skip that.
1605                     if ((((int)Math.ceil(end/2)) & 1) ==1 ) {
1606                         count++;
1607                     }
1608                     break;
1609                 }
1610             } else {
1611                 // Encoded mode
1612                 int alternate[] = { (values[count] & 0xf0) >> 4,
1613                                     values[count] & 0x0f };
1614                 for (int i=0; (i < value) && (l < width); i++) {
1615                     val[l++] = (byte)alternate[i & 1];
1616                 }
1617 
1618                 count++;
1619             }
1620 
1621             // If End-of-RLE data, then exit the while loop
1622             if (flag) {
1623                 break;
1624             }
1625         }
1626     }
1627 
1628     /** Decodes the jpeg/png image embedded in the bitmap using any jpeg
1629      *  ImageIO-style plugin.
1630      *
1631      * @param bi The destination <code>BufferedImage</code>.
1632      * @param bmpParam The <code>ImageReadParam</code> for decoding this
1633      *          BMP image.  The parameters for subregion, band selection and
1634      *          subsampling are used in decoding the jpeg image.
1635      */
1636 
1637     private BufferedImage readEmbedded(int type,
1638                               BufferedImage bi, ImageReadParam bmpParam)
1639       throws IOException {
1640         String format;
1641         switch(type) {
1642           case BI_JPEG:
1643               format = "JPEG";
1644               break;
1645           case BI_PNG:
1646               format = "PNG";
1647               break;
1648           default:
1649               throw new
1650                   IOException("Unexpected compression type: " + type);
1651         }
1652         ImageReader reader =
1653             ImageIO.getImageReadersByFormatName(format).next();
1654         if (reader == null) {
1655             throw new RuntimeException(I18N.getString("BMPImageReader4") +
1656                                        " " + format);
1657         }
1658         // prepare input
1659         byte[] buff = new byte[(int)imageSize];
1660         iis.read(buff);
1661         reader.setInput(ImageIO.createImageInputStream(new ByteArrayInputStream(buff)));
1662         if (bi == null) {
1663             ImageTypeSpecifier embType = reader.getImageTypes(0).next();
1664             bi = embType.createBufferedImage(destinationRegion.x +
1665                                              destinationRegion.width,
1666                                              destinationRegion.y +
1667                                              destinationRegion.height);
1668         }
1669 
1670         reader.addIIOReadProgressListener(new EmbeddedProgressAdapter() {
1671                 public void imageProgress(ImageReader source,
1672                                           float percentageDone)
1673                 {
1674                     processImageProgress(percentageDone);
1675                 }
1676             });
1677 
1678         reader.addIIOReadUpdateListener(new IIOReadUpdateListener() {
1679                 public void imageUpdate(ImageReader source,
1680                                         BufferedImage theImage,
1681                                         int minX, int minY,
1682                                         int width, int height,
1683                                         int periodX, int periodY,
1684                                         int[] bands)
1685                 {
1686                     processImageUpdate(theImage, minX, minY,
1687                                        width, height,
1688                                        periodX, periodY, bands);
1689                 }
1690                 public void passComplete(ImageReader source,
1691                                          BufferedImage theImage)
1692                 {
1693                     processPassComplete(theImage);
1694                 }
1695                 public void passStarted(ImageReader source,
1696                                         BufferedImage theImage,
1697                                         int pass,
1698                                         int minPass, int maxPass,
1699                                         int minX, int minY,
1700                                         int periodX, int periodY,
1701                                         int[] bands)
1702                 {
1703                     processPassStarted(theImage, pass, minPass, maxPass,
1704                                        minX, minY, periodX, periodY,
1705                                        bands);
1706                 }
1707                 public void thumbnailPassComplete(ImageReader source,
1708                                                   BufferedImage thumb) {}
1709                 public void thumbnailPassStarted(ImageReader source,
1710                                                  BufferedImage thumb,
1711                                                  int pass,
1712                                                  int minPass, int maxPass,
1713                                                  int minX, int minY,
1714                                                  int periodX, int periodY,
1715                                                  int[] bands) {}
1716                 public void thumbnailUpdate(ImageReader source,
1717                                             BufferedImage theThumbnail,
1718                                             int minX, int minY,
1719                                             int width, int height,
1720                                             int periodX, int periodY,
1721                                             int[] bands) {}
1722             });
1723 
1724         reader.addIIOReadWarningListener(new IIOReadWarningListener() {
1725                 public void warningOccurred(ImageReader source, String warning)
1726                 {
1727                     processWarningOccurred(warning);
1728                 }
1729             });
1730 
1731         ImageReadParam param = reader.getDefaultReadParam();
1732         param.setDestination(bi);
1733         param.setDestinationBands(bmpParam.getDestinationBands());
1734         param.setDestinationOffset(bmpParam.getDestinationOffset());
1735         param.setSourceBands(bmpParam.getSourceBands());
1736         param.setSourceRegion(bmpParam.getSourceRegion());
1737         param.setSourceSubsampling(bmpParam.getSourceXSubsampling(),
1738                                    bmpParam.getSourceYSubsampling(),
1739                                    bmpParam.getSubsamplingXOffset(),
1740                                    bmpParam.getSubsamplingYOffset());
1741         reader.read(0, param);
1742         return bi;
1743     }
1744 
1745     private class EmbeddedProgressAdapter implements IIOReadProgressListener {
1746         public void imageComplete(ImageReader src) {}
1747         public void imageProgress(ImageReader src, float percentageDone) {}
1748         public void imageStarted(ImageReader src, int imageIndex) {}
1749         public void thumbnailComplete(ImageReader src) {}
1750         public void thumbnailProgress(ImageReader src, float percentageDone) {}
1751         public void thumbnailStarted(ImageReader src, int iIdx, int tIdx) {}
1752         public void sequenceComplete(ImageReader src) {}
1753         public void sequenceStarted(ImageReader src, int minIndex) {}
1754         public void readAborted(ImageReader src) {}
1755     }
1756 
1757     private static Boolean isLinkedProfileDisabled = null;
1758 
1759     private static boolean isLinkedProfileAllowed() {
1760         if (isLinkedProfileDisabled == null) {
1761             PrivilegedAction<Boolean> a = new PrivilegedAction<Boolean>() {
1762                 public Boolean run() {
1763                     return Boolean.getBoolean("sun.imageio.plugins.bmp.disableLinkedProfiles");
1764                 }
1765             };
1766             isLinkedProfileDisabled = AccessController.doPrivileged(a);
1767         }
1768         return !isLinkedProfileDisabled;
1769     }
1770 
1771     private static Boolean isWindowsPlatform = null;
1772 
1773     /**
1774      * Verifies whether the byte array contans a unc path.
1775      * Non-UNC path examples:
1776      *  c:\path\to\file  - simple notation
1777      *  \\?\c:\path\to\file - long notation
1778      *
1779      * UNC path examples:
1780      *  \\server\share - a UNC path in simple notation
1781      *  \\?\UNC\server\share - a UNC path in long notation
1782      *  \\.\some\device - a path to device.
1783      */
1784     private static boolean isUncOrDevicePath(byte[] p) {
1785         if (isWindowsPlatform == null) {
1786             PrivilegedAction<Boolean> a = new PrivilegedAction<Boolean>() {
1787                 public Boolean run() {
1788                     String osname = System.getProperty("os.name");
1789                     return (osname != null &&
1790                             osname.toLowerCase().startsWith("win"));
1791                 }
1792             };
1793             isWindowsPlatform = AccessController.doPrivileged(a);
1794         }
1795 
1796         if (!isWindowsPlatform) {
1797             /* no need for the check on platforms except windows */
1798             return false;
1799         }
1800 
1801         /* normalize prefix of the path */
1802         if (p[0] == '/') p[0] = '\\';
1803         if (p[1] == '/') p[1] = '\\';
1804         if (p[3] == '/') p[3] = '\\';
1805 
1806 
1807         if ((p[0] == '\\') && (p[1] == '\\')) {
1808             if ((p[2] == '?') && (p[3] == '\\')) {
1809                 // long path: whether unc or local
1810                 return ((p[4] == 'U' || p[4] == 'u') &&
1811                         (p[5] == 'N' || p[5] == 'n') &&
1812                         (p[6] == 'C' || p[6] == 'c'));
1813             } else {
1814                 // device path or short unc notation
1815                 return true;
1816             }
1817         } else {
1818             return false;
1819         }
1820     }
1821 }